All Pog code executing in the game is executing as a task, and the script engine
allocates execution time to each task in turn. Flux provides the facility to start
script functions as tasks (using FcScriptEngine::StartTask
) or as
function calls (using FcScriptEngine::CallFunction
). Almost all
gameplay code executes as tasks, since it executes while the game is running -
calling a function while the game is running forces the entire game to wait for
the function to finish, which is not usually the desired behaviour.
// This is a normal function prototype prototype int normal_function( int parameter ); // This is a task function prototype prototype task task_function( int parameter ); |
Note:
The task keyword in front of the task function is not a return value, it is a
special declaration syntax to mark this function as a task function.
There are no other restrictions on task functions. They are in all other respects identical to normal functions.
Important:
A task function is not the same thing as a task. The task is the flow
of program execution, and the task function is the program which directs the flow.
Several tasks may start the same task function, but they are all different tasks.
As an analogy, the task function is like a road, and the task like a car - the road
guides the car, and the car represents progress along the road. Many cars can drive
on the same road, and a car can leave one road and drive on another. This is like
having a task function call other functions.
Tasks are executed by starting a task function. This uses the same syntax, but the name of the function is preceded by the keyword start. This forms a start expression whose type is htask. The value of the function start expression is the handle to the task which has been started. The code which starts the task may store this handle in a variable and use it to manipulate the task via the API in the Task package.
Calling task functions or starting normal functions is not possible. The execution type must match the function type. This prevents code which is intended to be executed atomically3 from being executed as a task, and vice-versa.
For example, a task function may include an infinite loop which waits for events to be signalled by the game. If the function were called atomically this loop would never receive events (time would not pass in the game while the loop executed) and the loop would not terminate. This would cause the task which had called the function to lock up.
The following example shows a task being started:
package TaskTest; uses Debug; provides Main; // This task function prints the numbers from 1 to n // to the log. task log_n_numbers( int n ) { int i; for ( i = 1; i < n; ++i ) Debug.PrintInt( i ); } // This function starts three different print tasks to // print different lists of numbers. Main() { start log_n_numbers( 10 ); start log_n_numbers( 20 ); start log_n_numbers( 30 ); } |
Running the Main
function in the example will start three tasks
using the task function log_n_numbers
. If you compile and run
this package, the debug log file will contain the numbers 1 to 10, 1 to 20,
and 1 to 30. The number sequences will probably be interleaved with other
because the tasks are all running at the same time and do not attempt to
synchronise their output.
handle htask : hobject; |
When a task is started from within Pog code, the handle to the task is provided automatically and appears to be a return value (although it is not really a return value - task functions have no return value.)
If you wish to access and manipulate the task after it has been created, you can store the task handle in a variable using an assignment in the usual way.
Tasks halt automatically when the flow of execution returns from the task function which was started to create the task. Tasks may be halted explicitly, if their handle is known, using the function Task.Halt. The current task may be halted in this manner if desired.
When a task halts, its child tasks (tasks which were started by that task) are also halted. If you need a child task to outlive its parent, then that task must be detached and made a new top level task, using the function Task.Detach.
2 Address spaces are the environments in which code is executed. The address space contains all the data which the executing code may access, or address.
3 Atomic execution is when a function is called as single operation. The function executes as soon as it is called, and control does not return to the point of call until the function has finished executing. This is the normal type of function execution.